package com.unswift.utils;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.unswift.annotation.api.Api;
import com.unswift.annotation.api.ApiField;
import com.unswift.annotation.api.ApiMethod;
import com.unswift.annotation.trim.DontTrim;
import com.unswift.annotation.trim.WantTrim;

@SuppressWarnings({ "unchecked", "rawtypes" })
@Api(value="对象公共操作类", author="unswift", date="2022-12-16", version="1.0.0")
public final class ObjectUtils {

	@ApiField("日志对象")
	private static final Logger logger=LoggerFactory.getLogger(ObjectUtils.class);
	
	@ApiMethod(value="判断对象是否为null", params=@ApiField("要判断的对象"), returns=@ApiField("是否为null，true：为null，false：不为null"))
	public static boolean isNull(Object source) {
		return source == null;
	}

	@ApiMethod(value="判断对象是否不为null", params=@ApiField("要判断的对象"), returns=@ApiField("是否不为null，true：不为null，false：为null"))
	public static boolean isNotNull(Object source) {
		return !isNull(source);
	}

	@ApiMethod(value="判断对象是否为null，如果是字符串包含为空，如果是列表或map，长度必须大于0，如果是数组，则长度必须大于0", params=@ApiField("要判断的对象"), returns=@ApiField("是否为空或null，true：为null或空，false：不为null或空"))
	public static boolean isEmpty(Object source) {
		if (source == null) {
			return true;
		}else if(source instanceof CharSequence){
			return "".equals(source);
		} else if (source instanceof Collection) {
			return ((Collection) source).size() == 0;
		} else if (source instanceof Map) {
			return ((Map) source).size() == 0;
		} else if (source.getClass().isArray()) {
			return Array.getLength(source) == 0;
		}
		return false;
	}

	@ApiMethod(value="判断对象是否不为null，如果是字符串包含为空，如果是列表或map，长度必须大于0，如果是数组，则长度必须大于0", params=@ApiField("要判断的对象"), returns=@ApiField("是否不为空或null，true：不为null或空，false：为null或空"))
	public static boolean isNotEmpty(Object source) {
		return !isEmpty(source);
	}
	
	@ApiMethod(value="初始化对象值，设置逻辑，如果对象为null，则返回初始值", params={@ApiField("对象"), @ApiField("初始值")}, returns=@ApiField("初始化后的对象值"))
	public static <T> T init(T source, T init) {
		if(isNull(source)) {
			return init;
		}
		return source;
	}
	
	@ApiMethod(value="初始化对象值，设置逻辑，如果对象为空，则返回初始值", params={@ApiField("对象"), @ApiField("初始值")}, returns=@ApiField("初始化后的对象值"))
	public static <T> T initEmpty(T source, T init) {
		if(isEmpty(source)) {
			return init;
		}
		return source;
	}
	
	@ApiMethod(value="判断两个对象是否相等", params={@ApiField("源对象"), @ApiField("目标对象"), @ApiField("是否区分大小写，true：不区分，false：区分")}, returns=@ApiField("是否相等，true：相等，false：不相等"))
	public static boolean equals(Object source, Object target, boolean ignore){
		if(isNull(source) && isNull(target)){
			return true;
		}
		if(isNull(source) || isNull(target)){
			return false;
		}
		if(ignore){
			return source.toString().equalsIgnoreCase(target.toString());
		}else{
			return source.equals(target);
		}
	}
	
	@ApiMethod(value="判断两个数组各元素是否相等", params={@ApiField("源数组"), @ApiField("目标数组"), @ApiField("是否区分大小写，true：不区分，false：区分")})
	public static boolean equals(String[] source, String[] target, boolean ignore){
		if(isEmpty(source) && isEmpty(target)){
			return true;
		}
		if(isNull(source) || isNull(target)){
			return false;
		}
		if(source.length!=target.length){
			return false;
		}
		boolean exists;
		int length=source.length;
		for (int i = 0; i < length; i++) {
			exists=false;
			for (int j = 0; j < length; j++) {
				if(equals(source[i], target[j], ignore)){
					exists=true;
					break;
				}
			}
			if(!exists){
				return false;
			}
		}
		for (int i = 0; i < length; i++) {
			exists=false;
			for (int j = 0; j < length; j++) {
				if(equals(target[i], source[j], ignore)){
					exists=true;
					break;
				}
			}
			if(!exists){
				return false;
			}
		}
		return true;
	}

	@ApiMethod(value="拷贝对象，对象类型必须是List或Map，否则返回自己", params=@ApiField("要拷贝的对象"), returns=@ApiField("拷贝后的对象"))
	public static Object copy(Object source) {
		if (isNull(source)) {
			return null;
		}
		if (source instanceof List) {
			return copyList((List<?>) source);
		} else if (source instanceof Map) {
			return copyMap((Map<?, ?>) source);
		}
		return source;
	}

	@ApiMethod(value="拷贝列表对象", params=@ApiField("要拷贝的列表对象"), returns=@ApiField("拷贝后的列表对象"))
	public static <V> List<V> copyList(List<V> source) {
		if (isNull(source)) {
			return null;
		}
		List<V> target = new ArrayList<V>();
		for (V value : source) {
			if (value instanceof Map) {
				target.add((V) copyMap((Map) value));
			} else if (value instanceof List) {
				target.add((V) copyList((List) value));
			} else {
				target.add(value);
			}
		}
		return target;
	}

	@ApiMethod(value="拷贝Map对象", params=@ApiField("要拷贝的Map对象"), returns=@ApiField("拷贝后的Map对象"))
	public static <K, V> Map<K, V> copyMap(Map<K, V> source) {
		if (isNull(source)) {
			return null;
		}
		Map<K, V> target = new HashMap<K, V>();
		Set<K> keys = source.keySet();
		V v;
		for (K key : keys) {
			v = source.get(key);
			if (v instanceof Map) {
				target.put(key, (V) copyMap((Map) v));
			} else if (v instanceof List) {
				target.put(key, (V) copyList((List) v));
			} else {
				target.put(key, v);
			}
		}
		return target;
	}
	
	@ApiMethod(value="将源对象的属性值拷贝到目标对象的属性里", params={@ApiField("源对象"), @ApiField("目标对象")})
	public static void copy(Object source, Object target){
		if(isEmpty(source) || isEmpty(target)){
			return ;
		}
		List<Field> fieldList = ClassUtils.getFieldList(target.getClass());
		for (Field field : fieldList) {
			if(ClassUtils.existsField(source, field.getName())){
				field.setAccessible(true);
				try {
					field.set(target, ClassUtils.get(source, field.getName()));
				} catch (Exception e) {
					logger.warn("对象拷贝异常："+e.getMessage());
				}
			}
		}
	}
	
	@ApiMethod(value="将源对象的属性值拷贝到目标对象的属性里", params={@ApiField("源对象"), @ApiField("目标对象"), @ApiField("源对象字段"), @ApiField("匹配目标字段")})
	public static void copy(Object source, Object target, String[] sourceField, String[] matchField){
		if(isEmpty(source) || isEmpty(target)){
			return ;
		}
		List<String> targetFieldSet=ObjectUtils.asList(matchField);
		List<Field> fieldList = ClassUtils.getFieldList(target.getClass());
		for (Field field : fieldList) {
			if(ClassUtils.existsField(source, field.getName())){
				field.setAccessible(true);
				try {
					if(targetFieldSet.contains(field.getName())) {
						int index=targetFieldSet.indexOf(field.getName());
						field.set(target, ClassUtils.get(source, sourceField[index]));
					}else {
						field.set(target, ClassUtils.get(source, field.getName()));
					}
				} catch (Exception e) {
					logger.warn("对象拷贝异常："+e.getMessage());
				}
			}
		}
	}
	
	@ApiMethod(value="将源Map中键值对拷贝到目标Map中", params={@ApiField("源Map对象"), @ApiField("目标Map对象")})
	public static void copyTargetNotNull(Map<String, Object> source, Map<String, Object> target){
		if(isEmpty(source) || isEmpty(target)){
			return ;
		}
		Set<String> keys=source.keySet();
		Object value;
		for (String key : keys) {
			value = source.get(key);
			if(!target.containsKey(key)){
				target.put(key, value);
			}else{
				if(value instanceof Map){
					copyTargetNotNull((Map<String, Object>)value, (Map<String, Object>)target.get(key));
				}else{
					target.put(key, value);
				}
			}
		}
	}
	
	@ApiMethod(value="将两个byte数组合并为一个数组", params={@ApiField("第一个数组"), @ApiField("第二个数组")}, returns = @ApiField("合并后的数组"))
	public static byte[] arrayMerge(byte[] array1, byte[] array2){
		int length=array1.length;
		if(length==0) {
			return array2;
		}
		int length2=array2.length;
		byte[] array3=new byte[length+length2];
		System.arraycopy(array1, 0, array3, 0, length);
		System.arraycopy(array2, 0, array3, length, length2);
		return array3;
	}
	
	@ApiMethod(value="将可变参数对象转为数组对象", params=@ApiField("可变参数对象"), returns=@ApiField("数组对象"))
	public static <T> T[] toArray(T...args){
		return args;
	}
	
	@ApiMethod(value="将可变参数反射类对象转为反射类数组对象", params=@ApiField("可变参数反射类对象"), returns=@ApiField("反射类数组对象"))
	public static Class<?>[] toArray(Class<?>...args){
		return ClassUtils.toArray(args);
	}
	
	@ApiMethod(value="将可变参数对象转为基础(Object)对象数组", params=@ApiField("可变参数对象"), returns=@ApiField("基础(Object)数组对象"))
	public static <T> Object[] toObjectArray(T...args){
		int length=args.length;
		Object[] target=new Object[length];
		for (int i = 0; i < length; i++) {
			target[i]=args[i];
		}
		return target;
	}
	
	@ApiMethod(value="将首个对象和可变参数对象转为基础(Object)数组对象", params={@ApiField("首个对象"), @ApiField("可变参数对象")}, returns=@ApiField("基础(Object)数组对象"))
	public static <T> Object[] toObjectArrayAdd(T first, T...args){
		int length=args.length;
		Object[] target=new Object[length+1];
		target[0]=first;
		for (int i = 0; i < length; i++) {
			target[i+1]=args[i];
		}
		return target;
	}
	
	@ApiMethod(value="将可变参数对象转为列表对象，说明：此列表对象是可以自由变动的", params=@ApiField("可变参数对象"), returns=@ApiField("列表对象"))
	public static <T> List<T> asList(T...args){
		if(ObjectUtils.isEmpty(args)){
			return null;
		}
		List<T> list=new ArrayList<T>();
		for (T item : args) {
			list.add(item);
		}
		return list;
	}
	
	@ApiMethod(value="将可变参数对象转为Set集合对象", params=@ApiField("可变参数对象"), returns=@ApiField("Set集合对象"))
	public static <T> Set<T> asSet(T...args){
		if(ObjectUtils.isEmpty(args)){
			return null;
		}
		Set<T> set=new HashSet<T>();
		for (T item : args) {
			set.add(item);
		}
		return set;
	}
	
	@ApiMethod(value="向列表末尾增加元素，如果追加内容为空，则不追加", params={@ApiField("列表对象"), @ApiField("追加的内容")})
	public static void listAdd(List list, Object item){
		if(isNotEmpty(item)){
			list.add(item);
		}
	}
	
	@ApiMethod(value="向列表末尾增加子列表，如果追加子列表为空，则不追加", params={@ApiField("列表对象"), @ApiField("追加的子列表对象")})
	public static void listAddAll(List list, List subList){
		if(isNotEmpty(subList)){
			list.addAll(subList);
		}
	}
	
	@ApiMethod(value="向列表末尾增加子列表，如果子列表元素已在列表中存在，则不追加", params={@ApiField("列表对象"), @ApiField("追加的子列表对象")})
	public static <T> void addNoExists(Set<T> list, List<T> subList){
		for (T item : subList) {
			if(!list.contains(item)){
				list.add(item);
			}
		}
	}
	
	@ApiMethod(value="判断字符串数组中是否存在指定元素，不区分大小写", params={@ApiField("字符串数组对象"), @ApiField("元素对象")}, returns=@ApiField("是否包含指定元素，true：包含，false：不包含"))
	public static boolean contains(String[] array, String target) {
		for (String item : array) {
			if(item.equalsIgnoreCase(target)){
				return true;
			}
		}
		return false;
	}
	
	@ApiMethod(value="判断整形数组中是否存在指定元素", params={@ApiField("整形数组对象"), @ApiField("元素对象")}, returns=@ApiField("是否包含指定元素，true：包含，false：不包含"))
	public static boolean contains(int[] array, int target) {
		for (int item : array) {
			if(item==target){
				return true;
			}
		}
		return false;
	}
	
	@ApiMethod(value="判断集合中是否存在指定元素，不区分大小写", params={@ApiField("集合对象"), @ApiField("元素对象")}, returns=@ApiField("是否包含指定元素，true：包含，false：不包含"))
	public static boolean contains(Collection<?> collection, String target) {
		for (Object item : collection) {
			if(ObjectUtils.isEmpty(item)){
				continue;
			}
			if(item.toString().equalsIgnoreCase(target)){
				return true;
			}
		}
		return false;
	}
	
	@ApiMethod(value="判断集合中元素的指定字段是否存在指定值，不区分大小写", params={@ApiField("集合对象"), @ApiField("指定字段名称"), @ApiField("元素对象")}, returns=@ApiField("是否包含指定值，true：包含，false：不包含"))
	public static boolean contains(Collection<?> collection, String matchField, String target) {
		for (Object item : collection) {
			if(ObjectUtils.isEmpty(item)){
				continue;
			}
			item=ClassUtils.get(item, matchField);
			if(item.toString().equalsIgnoreCase(target)){
				return true;
			}
		}
		return false;
	}
	
	@ApiMethod(value="判断字符串数组中是否存在指定列表中的任何一个，区分大小写", params={@ApiField("字符串对象"), @ApiField("包含的列表对象")}, returns=@ApiField("是否包含指定元素，true：包含，false：不包含"))
	public static boolean containsOnce(String source, String...subArray) {
		for (String item : subArray) {
			if(source.contains(item)){
				return true;
			}
		}
		return false;
	}
	
	@ApiMethod(value="执行JavaScript脚本", params= {@ApiField("JavaScript脚本"), @ApiField("转换为指定类型")}, returns=@ApiField("JavaScript脚本的返回值"))
	public static <T> T evalJs(String script, Class<?> tclass){
		ScriptEngineManager manager = new ScriptEngineManager();
		ScriptEngine engine = manager.getEngineByName("nashorn");
		try {
			Object eval=engine.eval(script);
			if(ObjectUtils.isNull(eval)){
				return (T)eval;
			}
			return (T)ClassUtils.stringToBaseJava(eval.toString(), tclass);
		} catch (ScriptException e) {
			e.printStackTrace();
			throw ExceptionUtils.exception("execute.script.exception", e, script, e.getMessage());
		}
	}

	@ApiMethod(value="根据列表对象的某个字段去重，如根据Message的code字段去重，则调用方法为：ObjectUtils.distinct(list, \"code\")", 
			params={@ApiField("列表数据"), @ApiField("去重的字段")}, 
			returns=@ApiField("去重后的数据"))
	public static <T> List<T> distinct(List<T> list, String fieldName){
		if(isNotEmpty(list)){
			List<T> resultList=new ArrayList<T>();
			Map<Object, T> havingMap=new HashMap<Object, T>();
			Object value;
			for (T t : list) {
				value=ClassUtils.get(t, fieldName);
				if(!havingMap.containsKey(value)){
					havingMap.put(value, t);
					resultList.add(t);
				}
			}
			return resultList;
		}else{
			return list;
		}
	}

	@ApiMethod(value="对对象或对象属性进行去空操作", params = {@ApiField("需要去空的对象"), @ApiField("pojo顶层类"), @ApiField("去空规则，true：遇到@WantTrim注解去空，false：遇到@DontTrim注解不去空")}, returns = @ApiField("去空后的对象"))
	public static <T> T trim(T source, Class<?> superPojoClass, boolean trimRule) {
		if(ObjectUtils.isEmpty(source)) {
			return source;
		}
		if(source instanceof String) {
			return (T)StringUtils.trim((String)source);
		}else if(source.getClass().isArray()) {
			int length=Array.getLength(source);
			for (int i = 0; i < length; i++) {
				Array.set(source, i, trim(Array.get(source, i), superPojoClass, trimRule));
			}
		}else if(source instanceof List) {
			List<?> list=(List<?>)source;
			List newList=new ArrayList<>();
			for (Object item : list) {
				newList.add(trim(item, superPojoClass, trimRule));
			}
			source=(T)newList;
		}else if(source instanceof Set) {
			Set<?> set=(Set<?>)source;
			Set newSet=new HashSet<>();
			for (Object item : set) {
				newSet.add(trim(item, superPojoClass, trimRule));
			}
			source=(T)newSet;
		}else if(source instanceof Map) {
			Map map=(Map)source;
			Set<?> keys=map.keySet();
			for (Object key : keys) {
				map.put(key, (Object)trim(map.get(key), superPojoClass, trimRule));
			}
		}else if(ObjectUtils.isNotEmpty(superPojoClass) && ClassUtils.isSuperClass(source.getClass(), superPojoClass)) {
			List<Field> fieldList=ClassUtils.getFieldList(source.getClass());
			for (Field field : fieldList) {
				if(trimRule && ObjectUtils.isNotEmpty(field.getAnnotation(WantTrim.class))) {
					ClassUtils.set(source, field, trim(ClassUtils.get(source, field), superPojoClass, trimRule));
				}else if(!trimRule && ObjectUtils.isEmpty(field.getAnnotation(DontTrim.class))){
					ClassUtils.set(source, field, trim(ClassUtils.get(source, field), superPojoClass, trimRule));
				}
			}
		}
		return source;
	}
}
